#include "PipeTask.hpp"
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

#include <iostream>
#include <vector>
#include <cassert>
#include <time.h>
#include <cstdlib>

std::vector<task_t> tasks;

// 先描述，再组织
// 先描述
class channel
{
public:
    channel(int cmdfd, pid_t slaverid, std::string processname)
        : _cmdfd(cmdfd), _slaverid(slaverid), _processname(processname)
    {}

public:
    int _cmdfd;               // 发送任务的文件描述符
    pid_t _slaverid;          // 子进程的pid
    std::string _processname; // 子进程的名字 -- 为了方标打印日志

    static int process_allnum;
};

int channel::process_allnum = 10;

// 子进程执行的功能
void Slaver()
{
    // std::cout<< "---" <<std::endl;
    while (true)
    {
        int cmdcode = 0;
        int n = read(0, &cmdcode, sizeof(int));     // 存疑
        if(n == sizeof(int))
        {
            // 执行 cmdcode 对应的任务列表
            std::cout << "slaver say> get a command: " << getpid() <<
                " : cmdcode: " << cmdcode << std::endl;
            if(cmdcode >= 0 && cmdcode < tasks.size()) tasks[cmdcode]();
        }
        if(n == 0) break;
        sleep(1);
    }
}

// 菜单
void Menu()
{
    std::cout << "#################################################" << std::endl;
    std::cout << "# 1. 刷新日志               2. 刷新野区          #" << std::endl;
    std::cout << "# 3. 检测软件是否更新        4. 更新用户血量和蓝量 #" << std::endl;
    std::cout << "# 5. 每日签到               0. 退出             #" << std::endl;
    std::cout << "############################################" << std::endl;
}

namespace Step
{
    void InitProcessPool(std::vector<channel> *channels)
    {
        for (size_t i = 0; i < channel::process_allnum; i++)
        {
            int pipefd[2]; // 临时空间
            int n = pipe(pipefd);
            assert(!n);
            (void)n;

            // 开始创建子进程
            pid_t id = fork();
            if (id < 0)
                return; // 返回错误码
            else if (id == 0)
            {
                // child
                close(pipefd[1]);   // 将写关掉
                dup2(pipefd[0], 0); // dup2的用法需要加深理解
                close(pipefd[0]);

                // IPC code
                Slaver(); // 子进程执行的功能
                // std::cout << "process: " << getpid() << " qiut" << std::endl;
                // 若不使用 dup2 函数用法，也可 Slaver(pipefd[0]);
                exit(0);
            }
            // father
            close(pipefd[0]); // 将读关闭

            // 开始添加 channel字段
            std::string name = "process-" + std::to_string(i);
            channels->push_back(channel(pipefd[1], id, name));
            // sleep(1);
        }
    }

    void CtrlSlaver(std::vector<channel> &channels)
    {
        while (true)
        {
            // // 法一：随机
            // // 选择任务
            // int cmdcode = rand() % 20; // 获取的随机数(表示任务)
            // // 选择进程
            // int processpos = rand() % channels.size(); // 完成任务随机获得的进程
            // std::cout << "father say: " << " cmdcode: " << cmdcode << " already sendto " << channels[processpos]._slaverid << " process name: " << channels[processpos]._processname << std::endl;
            // sleep(5);
            // // 发送任务
            // write(channels[processpos]._cmdfd, &cmdcode, sizeof(cmdcode));

            // 法二：轮转
            // 选择任务
            int cmdcode = rand() % tasks.size();
            // 选择进程
            int processpos = rand() % channels.size();
            // 发送任务
            write(channels[processpos]._cmdfd, &cmdcode, sizeof(cmdcode));
        }
    }

    void QuitProcess(const std::vector<channel> &channels)
    {
        for(const auto& c : channels) {close(c._cmdfd);}
        sleep(3);
        for(const auto& c : channels) {waitpid(c._slaverid, nullptr, 0);}
        sleep(3);
    }
}

// 测试
void Debug(const std::vector<channel> &channels)
{
    for (const auto &e : channels)
    {
        std::cout << e._cmdfd << " " << e._slaverid << " " << e._processname << std::endl;
    }
}

int main()
{
    // 任务说明
    LoadTask(&tasks);

    srand(time(nullptr) ^ getpid() ^ 1023); // 种一个随机数的种子
    // 再组织, father->w , child->r
    std::vector<channel> channels;

    // 初始化
    Step::InitProcessPool(&channels);
    // Debug(channels);

    // 开始控制子进程
    //Step::CtrlSlaver(channels);

    // 清理收尾
    // Step::QuitProcess(channels);

    return 0;
}
